package nic.view;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.Locale;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ExecutionException;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.filechooser.FileFilter;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableRowSorter;

import nic.controller.QNapController;
import nic.controller.Utils;
import nic.controller.QNapController.GetConfigurationNodeVisitor;
import nic.controller.QNapController.NetworkShare;
import nic.controller.QNapController.TorrentDownloadState;
import nic.swingutils.SwingWorker;
import java.awt.Dimension;
import javax.swing.JMenuBar;
import javax.swing.JMenu;
import javax.swing.JMenuItem;

import org.apache.commons.lang.ArrayUtils;

public class Mainframe extends JFrame {

	private QNapController controller = new QNapController();
	
	private static final long serialVersionUID = 1L;

	private JPanel jContentPane = null;

	private JTabbedPane jTabbedPane = null;

	private JTable jTableRunning = null;
	private JTable jTablePaused = null;
	private JTable jTableFinished = null;
	
	private JScrollPane jScrollPaneRunning = null;
	private JScrollPane jScrollPanePaused = null;
	private JScrollPane jScrollPaneFinished = null;

	private JPanel jPanel = null;

	private JPanel jPanel1 = null;

	private JButton upJButton = null;

	private JButton downJButton = null;

	//	Create a file chooser
	final JFileChooser jAddFileChooser = new JFileChooser();

	private JButton moveToJButton = null;
	
	private Timer timer;

	private JButton pauseJButton = null;

	private JButton resumeJButton = null;

	private JButton infoJButton = null;

	private JButton deleteJButton = null;

	private JButton deleteAllJButton = null;

	private JMenuBar jJMenuBar = null;

	private JMenu settingsJMenu = null;
	private JMenu aboutJMenu = null;

	private JMenuItem configurationJMenuItem = null;
//	private JMenuItem serverConfigurationJMenuItem = null;

	private JMenuItem shareTimeJMenuItem = null;
	
	private JMenuItem setServerJMenuItem = null;

	private JMenu taskJMenu = null;

	private JMenuItem addBtJMenuItem = null;

	private JMenuItem addFtpJMenuItem = null;
	
	Comparator<String> dateComparator = new Comparator<String>() {
	    public int compare(String str1, String str2) {
	    	try
	    	{
	    		Date date1 = parseDate(str1);
	    		Date date2 = parseDate(str2);
	    		return date1.compareTo(date2);
	    	} catch (Exception e)
	    	{
	    		e.printStackTrace();
	    	}
	    	return 0;
	    }
	    
	    private Date parseDate(String timestamp)
	    	throws Exception {
	    	/*
	    	 ** we specify Locale.US since months are in english
	    	 */
	    	SimpleDateFormat sdf = new SimpleDateFormat(
	    			"EEE MMM d HH:mm:ss yyyy", Locale.US);
	    	return sdf.parse(timestamp);
	    }
	};
	
	Comparator<String> etaComparator = new Comparator<String>() {
	    public int compare(String str1, String str2) {
	    	try
	    	{
	    		Integer etaSeconds1 = -1;
	    		int etaSeconds2 = -1;
	    		
	    		if (str1.isEmpty() == false)
	    		{
	    			etaSeconds1 = parseEta(str1);
	    		}
	    		if (str2.isEmpty() == false)
	    		{
	    			etaSeconds2 = parseEta(str2);
	    		}
	    		return etaSeconds1.compareTo(etaSeconds2);
	    	} catch (Exception e)
	    	{
	    		e.printStackTrace();
	    	}
	    	return 0;
	    }
	    
	    private int parseEta(String timestamp)
	    	throws Exception {
	    	/*
	    	 ** we specify Locale.US since months are in english
	    	 */
	    	String[] times = timestamp.split(":");
	    	int seconds = Integer.parseInt(times[2]) + Integer.parseInt(times[1]) * 60
	    		+ Integer.parseInt(times[0]) * 3600;
	    	return seconds;
	    }
	};

	/**
	 * This method initializes jPanel	
	 * 	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getBottomJPanel() {
		if (jPanel == null) {
			jPanel = new JPanel();
			jPanel.setLayout(new FlowLayout());
		}
		return jPanel;
	}

	/**
	 * This method initializes jPanel1	
	 * 	
	 * @return javax.swing.JPanel	
	 */
	private JPanel getTopJPanel() {
		if (jPanel1 == null) {
			jPanel1 = new JPanel();
			jPanel1.setLayout(new FlowLayout());
			jPanel1.add(getUpJButton());
			jPanel1.add(getDownJButton(), null);
			jPanel1.add(getMoveToJButton(), null);
			jPanel1.add(getPauseJButton(), null);
			jPanel1.add(getResumeJButton(), null);
			jPanel1.add(getInfoJButton(), null);
			jPanel1.add(getDeleteJButton(), null);
			jPanel1.add(getDeleteAllJButton(), null);
		}
		return jPanel1;
	}

	/**
	 * This method initializes jButton2	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getUpJButton() {
		if (upJButton == null) {
			upJButton = new JButton();
			upJButton.setText("Up");
			upJButton.setToolTipText("<html>Moves the selected task(s) up by one position in the list.</html>");
			upJButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {
					moveUpSelectedTasks();
					getRunningList();
				}
			});
		}
		return upJButton;
	}

	private void moveUpSelectedTasks()
	{
		int[] selectedTasks = getVisibleTable().getSelectedRows();
		for (int taskIndex : selectedTasks)
		{
			this.controller.moveTaskUp(getVisibleTable().convertRowIndexToModel(taskIndex));
		}
	}
	
	private void moveDownSelectedTasks()
	{
		int[] selectedTasks = getJTableRunning().getSelectedRows();
		for (int taskIndex : selectedTasks)
		{
			this.controller.moveTaskDown(getVisibleTable().convertRowIndexToModel(taskIndex));
		}
	}
	
	/**
	 * This method initializes jButton3	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getDownJButton() {
		if (downJButton == null) {
			downJButton = new JButton();
			downJButton.setText("Down");
			downJButton.setToolTipText("<html>Moves the selected task(s) down by one position in the list.</html>");
			downJButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {
					moveDownSelectedTasks();
					getRunningList();
				}
			});
		}
		return downJButton;
	}

	/**
	 * This method initializes jButton4	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getMoveToJButton() {
		if (moveToJButton == null) {
			moveToJButton = new JButton();
			moveToJButton.setText("Move");
			moveToJButton.setMnemonic(KeyEvent.VK_M);
			moveToJButton.setToolTipText("<html>Moves the selected tasks to the specified position.<br />" +
					"Take care, as this this moves the tasks consecutively by one position until they reach their target position. " +
					"<br /><b>During this time, the application is not responding.</b></html>");
			moveToJButton.setActionCommand("Move");
			moveToJButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {
					int[] selectedTasks = getJTableRunning().getSelectedRows();
					
					String selected = "";
					for (int selectedTask : selectedTasks){
						Object[] possibilities = new Object[getJTableRunning().getRowCount() - 1];
						int j = 0;
						for (int i = 0; i < getJTableRunning().getRowCount(); i++) {
							if (i != selectedTask)
							{
								possibilities[j] = i + 1;
								if (selected.equals(""))
								{
									selected = Integer.toString(i + 1);
								}
								j++;
							}
						}
						Object result = JOptionPane.showInputDialog(
								Mainframe.this,
			                    "New position:",
			                    "Customized Dialog",
			                    JOptionPane.PLAIN_MESSAGE,
			                    null,
			                    possibilities,
			                    selected);
						if (result != null)
						{
							Integer newPos = (Integer)result - 1;
							Mainframe.this.controller.moveTaskTo(
									getVisibleTable().convertRowIndexToModel(selectedTask),
									newPos.intValue());
							getRunningList();
							
						}
					}
				}
			});
		}
		return moveToJButton;
	}

	/**
	 * This method initializes pauseJButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getPauseJButton() {
		if (pauseJButton == null) {
			pauseJButton = new JButton();
			pauseJButton.setText("Pause");
			pauseJButton.setMnemonic(KeyEvent.VK_P);
			pauseJButton.setToolTipText("<html>Pauses downloading the selected task(s) and moves them into the paused downloads tab.</html>");
			pauseJButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {
					
					int[] selectedTasks = getJTableRunning().getSelectedRows();
					for (int taskIndex : selectedTasks)
					{
						Mainframe.this.controller.pauseTask(
								getVisibleTable().convertRowIndexToModel(taskIndex));
					}
					
				}
			});
		}
		return pauseJButton;
	}

	/**
	 * This method initializes restartJButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getResumeJButton() {
		if (resumeJButton == null) {
			resumeJButton = new JButton();
			resumeJButton.setText("Resume");
			resumeJButton.setMnemonic(KeyEvent.VK_A);
			resumeJButton.setToolTipText("<html>Resumes downloading the selected paused download task(s).</html>");
			resumeJButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {
					int[] selectedTasks = getVisibleTable().getSelectedRows();
					QNapController.TorrentDownloadState state = getActiveState();
					for (int taskIndex : selectedTasks)
					{
						Mainframe.this.controller.restartTask(
								getVisibleTable().convertRowIndexToModel(taskIndex), state);
					}
				}
			});
		}
		return resumeJButton;
	}
	
	private TorrentDownloadState getActiveState() {
		int selectedTab = getJTabbedPane().getSelectedIndex();
		switch (selectedTab)
		{
		case 0:
			return TorrentDownloadState.Running;
		case 1:
			return TorrentDownloadState.Paused;
		case 2:
			return TorrentDownloadState.Finished;
		default:
			return null;	
		}
	}

	/**
	 * This method initializes infoJButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getInfoJButton() {
		if (infoJButton == null) {
			infoJButton = new JButton();
			infoJButton.setText("Info");
			infoJButton.setMnemonic(KeyEvent.VK_I);
			infoJButton.setToolTipText("<html>Shows diagnostic information about the selected task. May be used to see why an active task is not downloading anything.</html>");
			infoJButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {
					QNapController.TorrentDownloadState state = getActiveState();
					int[] selectedTasks = getVisibleTable().getSelectedRows();
					for (int taskIndex : selectedTasks)
					{
						String info = Mainframe.this.controller
							.getTaskDownloadInfo(taskIndex, state);
						
						JOptionPane.showMessageDialog(
								Mainframe.this, info);
					}
				}
			});
		}
		return infoJButton;
	}

	/**
	 * This method initializes deleteJButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getDeleteJButton() {
		if (deleteJButton == null) {
			deleteJButton = new JButton();
			deleteJButton.setText("Delete");
			deleteJButton.setMnemonic(KeyEvent.VK_D);
			deleteJButton.setToolTipText("<html>Removes a task from the list. You can choose to also delete all files that have been downloaded already.</html>");
			deleteJButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {
					int[] selectedTasks = getVisibleTable().getSelectedRows();
					QNapController.TorrentDownloadState page = getActiveState();
					for (int taskIndex : selectedTasks)
					{
						Object[] options = {"Keep files",
			                    "Delete files",
			                    "Cancel"};
						int option = JOptionPane.showOptionDialog(Mainframe.this,
						    "Do you want to keep the files already downloaded?",
						    "Remove download task",
						    JOptionPane.YES_NO_CANCEL_OPTION,
						    JOptionPane.QUESTION_MESSAGE,
						    null,
						    options,
						    options[0]);
					
						if (option == 0)
						{
							Mainframe.this.controller.deleteTask(
									getVisibleTable().convertRowIndexToModel(taskIndex),
									page, false);
						} else if (option == 1)
						{
							Mainframe.this.controller.deleteTask(
									getVisibleTable().convertRowIndexToModel(taskIndex),
									page, true);
						}
					}
				}
			});
		}
		return deleteJButton;
	}
	
	/**
	 * This method initializes deleteAllJButton	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getDeleteAllJButton() {
		if (deleteAllJButton == null) {
			deleteAllJButton = new JButton();
			deleteAllJButton.setText("Delete (all)");
			deleteAllJButton.setMnemonic(KeyEvent.VK_A);
			deleteAllJButton.setToolTipText("<html>Removes all selected tasks from the list. You can choose to also delete all files that have been downloaded already.</html>");
			deleteAllJButton.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mouseClicked(java.awt.event.MouseEvent e) {
					int[] selectedTasks = getVisibleTable().getSelectedRows();
					QNapController.TorrentDownloadState page = getActiveState();

					Object[] options = {"Keep files",
		                    "Delete files",
		                    "Cancel"};
					int option = JOptionPane.showOptionDialog(Mainframe.this,
					    "Do you want to keep the files already downloaded?\n" +
					    "(This will be applied to all selected downloads).",
					    "Remove download tasks",
					    JOptionPane.YES_NO_CANCEL_OPTION,
					    JOptionPane.QUESTION_MESSAGE,
					    null,
					    options,
					    options[0]);
					// iterate through the list in reverse order to prevent the service from removing items not
					// selected, due to changing id's (issue=22)
					Arrays.sort(selectedTasks);
					for (int i = selectedTasks.length - 1; i >= 0; i--)
					{
						if (option == 0)
						{
							Mainframe.this.controller.deleteTask(
									getVisibleTable().convertRowIndexToModel(selectedTasks[i]),
									page, false);
						} else if (option == 1)
						{
							Mainframe.this.controller.deleteTask(
									getVisibleTable().convertRowIndexToModel(selectedTasks[i]),
									page, true);
						}
					}
				}
			});
		}
		return deleteAllJButton;
	}
	/**
	 * This method initializes jJMenuBar	
	 * 	
	 * @return javax.swing.JMenuBar	
	 */
	private JMenuBar getJJMenuBar() {
		if (jJMenuBar == null) {
			jJMenuBar = new JMenuBar();
			jJMenuBar.setPreferredSize(new Dimension(0, 20));
			jJMenuBar.add(getTaskJMenu());
			jJMenuBar.add(getSettingsJMenu());
			jJMenuBar.add(getAboutJMenu());
		}
		return jJMenuBar;
	}

	/**
	 * This method initializes settingsJMenu	
	 * 	
	 * @return javax.swing.JMenu	
	 */
	private JMenu getSettingsJMenu() {
		if (settingsJMenu == null) {
			settingsJMenu = new JMenu();
			settingsJMenu.setText("Settings");
			settingsJMenu.add(getConfigurationJMenuItem());
			settingsJMenu.add(getSetServerJMenuItem());
			settingsJMenu.add(getShareTimeJMenuItem());
		}
		return settingsJMenu;
	}
	
	/**
	 * This method initializes settingsJMenu	
	 * 	
	 * @return javax.swing.JMenu	
	 */
	private JMenu getAboutJMenu() {
		if (aboutJMenu == null) {
			aboutJMenu = new JMenu();
			aboutJMenu.setText("About");
			aboutJMenu.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mousePressed(java.awt.event.MouseEvent e) {
					JOptionPane.showMessageDialog(Mainframe.this,
	                    "JGet v0.2\nCopyright (C) 2008 - 2010\n\nNicolas Lefebvre\n\nThijs Brobbel");
				}
			});
		}
		return aboutJMenu;
	}
	
//	private JMenuItem getServerConfigurationJMenuItem() {
//		if (serverConfigurationJMenuItem == null) {
//			serverConfigurationJMenuItem = new JMenuItem();
//			serverConfigurationJMenuItem.setText("Server configuration");
//			serverConfigurationJMenuItem.addMouseListener(new java.awt.event.MouseAdapter() {   
//				public void mousePressed(java.awt.event.MouseEvent e) {    
//					showDownloadServerDialog();
//				}
//			});
//		}
//		return configurationJMenuItem;
//	}

	/**
	 * This method initializes configurationJMenuItem	
	 * 	
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getConfigurationJMenuItem() {
		if (configurationJMenuItem == null) {
			configurationJMenuItem = new JMenuItem();
			configurationJMenuItem.setText("Configuration");
			configurationJMenuItem.addMouseListener(new java.awt.event.MouseAdapter() {   
				public void mousePressed(java.awt.event.MouseEvent e) {    
					GetConfigurationNodeVisitor visitor
						= Mainframe.this.controller.getConfiguration();
					ConfigurationDialog dialog = new ConfigurationDialog(Mainframe.this);
					dialog.setUpRate(visitor.getMaxUploadRate());
					dialog.setDownRate(visitor.getMaxDownloadRate());
					dialog.setNatPortForwarding(visitor.isNatPortForwarding());
					dialog.setMaxDownloads(visitor.getMaxConcurrentDownloads());
					dialog.setPortRangeStart(visitor.getBtPortRangeStart());
					dialog.setPortRangeEnd(visitor.getBtPortStartEnd());
					
					dialog.setVisible(true);
					
					if (dialog.isOk())
					{
						Mainframe.this.controller.setConfiguration(
								dialog.getMaxDownloads(), dialog.getDownRate(),
								dialog.getUpRate(), dialog.getPortRangeStart(),
								dialog.getPortRangeEnd(), dialog.isNatPortForwarding());
					}
				}
			});
		}
		return configurationJMenuItem;
	}

	/**
	 * This method initializes shareTimeJMenuItem	
	 * 	
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getShareTimeJMenuItem() {
		if (shareTimeJMenuItem == null) {
			shareTimeJMenuItem = new JMenuItem();
			shareTimeJMenuItem.setText("Set task's seeding time");
			shareTimeJMenuItem.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mousePressed(java.awt.event.MouseEvent e) {
					int[] selectedTasks = getJTableRunning().getSelectedRows();
					QNapController.TorrentDownloadState page = getActiveState();
					for (int taskIndex : selectedTasks)
					{
						String result = (String)JOptionPane.showInputDialog(
		                    Mainframe.this,
		                    "Seeding time after completion of download task:",
		                    "Edit seeding time",
		                    JOptionPane.PLAIN_MESSAGE,
		                    null,
		                    null,
		                    Mainframe.this.controller.getShareTime(
		                    		getVisibleTable().convertRowIndexToModel(taskIndex), page));
						
//						If a string was returned, say so.
						if ((result != null) && (result.length() > 0)) {
						try {
							Mainframe.this.controller.setShareTime(
									getVisibleTable().convertRowIndexToModel(taskIndex), page, 
									Integer.parseInt(result));
							} catch (NumberFormatException nfe) {
							   nfe.printStackTrace();
							}
						    return;
						}
					}
				}
			});
		}
		return shareTimeJMenuItem;
	}

	/**
	 * This method initializes taskJMenu	
	 * 	
	 * @return javax.swing.JMenu	
	 */
	private JMenu getTaskJMenu() {
		if (taskJMenu == null) {
			taskJMenu = new JMenu();
			taskJMenu.setText("Task");
			taskJMenu.add(getAddBtJMenuItem());
			taskJMenu.add(getAddFtpJMenuItem());
		}
		return taskJMenu;
	}

	/**
	 * This method initializes addBtJMenuItem	
	 * 	
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getAddBtJMenuItem() {
		if (addBtJMenuItem == null) {
			addBtJMenuItem = new JMenuItem();
			addBtJMenuItem.setText("Add torrent ...");
			addBtJMenuItem.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mousePressed(java.awt.event.MouseEvent e) {
					jAddFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
					jAddFileChooser.addChoosableFileFilter(new TorrentFileFilter());
					jAddFileChooser.setAcceptAllFileFilterUsed(false);
					int returnVal = jAddFileChooser.showOpenDialog(Mainframe.this);
					if (returnVal == JFileChooser.APPROVE_OPTION) {
						File file = jAddFileChooser.getSelectedFile();
						// Add the torrent to the server
						Mainframe.this.controller.addTorrent(file);
						Mainframe.this.controller.getList(TorrentDownloadState.Running);
					}
				}
			});
		}
		return addBtJMenuItem;
	}
	
	/**
	 * This method initializes addBtJMenuItem	
	 * 	
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getSetServerJMenuItem() {
		if (setServerJMenuItem == null) {
			setServerJMenuItem = new JMenuItem();
			setServerJMenuItem.setText("Server configuration");
			setServerJMenuItem.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mousePressed(java.awt.event.MouseEvent e) {
					Mainframe.this.showDownloadServerDialog();
				}

				
			});
		}
		return setServerJMenuItem;
	}
	
	private boolean showDownloadServerDialog() {
		DownloadServerDialog dialog = new DownloadServerDialog(
				Mainframe.this, Mainframe.this.controller);
		
		dialog.setVisible(true);
		
		if (dialog.isOk())
		{
			this.controller.setServer(
					dialog.getServerAddress(), dialog.getUsername(),
					dialog.getPassword(), dialog.getPort());
			if (dialog.doRememberCredentials())
			{
				this.controller.saveCredentials();
			}
		}
		return dialog.isOk();
	}

	/**
	 * This method initializes addFtpJMenuItem	
	 * 	
	 * @return javax.swing.JMenuItem	
	 */
	private JMenuItem getAddFtpJMenuItem() {
		if (addFtpJMenuItem == null) {
			addFtpJMenuItem = new JMenuItem();
			addFtpJMenuItem.setText("Add FTP/HTTP ...");
			addFtpJMenuItem.addMouseListener(new java.awt.event.MouseAdapter() {
				public void mousePressed(java.awt.event.MouseEvent e) {
					showAddFtpDialog(null);
				}
			});
		}
		return addFtpJMenuItem;
	}
	
	private void showAddFtpDialog(final String url) {
		Vector<NetworkShare> networkShares
			= Mainframe.this.controller.getNetworkShares();
		AddFtpDialog dialog = new AddFtpDialog(Mainframe.this, networkShares, url);
		
		dialog.setVisible(true);
		
		if (dialog.isOk())
		{
			Mainframe.this.controller.addFtp(
					dialog.getUrl(), dialog.getSaveFolder(),
					dialog.useCredentials(), dialog.getUsername(),
					dialog.getPassword());
		}
	}

	public static void main(String[] args) {

		// HttpClient logging
//		System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");
//		System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");
//		System.setProperty("org.apache.commons.logging.simplelog.log.httpclient.wire", "debug");
//		System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient", "debug");
		
//		Ask for window decorations provided by the look and feel.
		//JFrame.setDefaultLookAndFeelDecorated(true);
		
		URL url2 = null;
		try {
			url2 = new URL("ftp://username:password@ftp.whatever.com/file.zip;type=i");
			
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		String test = url2.getUserInfo();
		
//		1. Create the frame.
		Mainframe mainFrame = new Mainframe("JGet");

//		2. Optional: What happens when the frame closes?
		mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

//		4. Size the frame.
		mainFrame.pack();

//		5. Show it.
		mainFrame.setVisible(true);
		
		// Check the arguments
		if (args.length > 0)
		{
			boolean exitAfterTaskAdd = false;
			
			for (int i = 0; i < args.length; i++) {
//				Object result = JOptionPane.showInputDialog("Args " + i + ": " + args[i]
//				                                                                    + " ext: "
//				                                                                  + getFileExtension(args[i]));
				
				if (args[i].equalsIgnoreCase("?")
					|| args[i].equalsIgnoreCase("-h")
					|| args[i].equalsIgnoreCase("--help")
					|| args[i].equalsIgnoreCase("h")
					|| args[i].equalsIgnoreCase("help"))
				{
					System.out.println("Arguments:");
					System.out.println("argument starting with ftp\tStart downloading the specified ftp url");
					System.out.println("argument starting with http\tStart downloading the specified http url");
					System.out.println("argument ending with .torrent\tStart downloading the specified torrent");
					System.out.println("exit\tExit application after having started the download tasks specified in the command line");
					System.out.println("? or -h or --help or h or help\tShow this message");
				}
				else if (args[i].equalsIgnoreCase("exit"))
				{
					exitAfterTaskAdd = true;
				} else if (args[i].startsWith("http")
						|| args[i].startsWith("ftp"))
				{
					mainFrame.showAddFtpDialog(args[i]);
				} else if (getFileExtension(args[i]).equalsIgnoreCase("torrent"))
				{
					File file = new File(args[i]);
					if (file.exists())
					{
						mainFrame.controller.addTorrent(file);
					}
				}
			}
			
			if (exitAfterTaskAdd)
			{
				System.exit(0);
			}
		}
	}
	
	private static String getFileExtension(String fileName) {
		// getting the extension of a filename, (plain or including dirname)
		// This code is much faster than any regex technique.

		// filename without the extension
		String choppedFilename;

		// extension without the dot
		String ext;

		// where the last dot is. There may be more than one.
		int dotPlace = fileName.lastIndexOf('.');

		if (dotPlace >= 0) {
			// possibly empty
			choppedFilename = fileName.substring(0, dotPlace);

			// possibly empty
			ext = fileName.substring(dotPlace + 1);
		} else {
			// was no extension
			choppedFilename = fileName;
			ext = "";
		}

		return ext;
	}
	
	/**
	 * @param owner
	 */
	public Mainframe(String title) {
		super(title);
		if (this.controller.checkServer() == false
				&& showDownloadServerDialog() == false)
		{
			System.exit(0);
		}
		initialize();
	}

	/**
	 * This method initializes this
	 * 
	 * @return void
	 */
	private void initialize() {
		this.setSize(637, 214);
		this.setJMenuBar(getJJMenuBar());
		this.setContentPane(getJContentPane());
		
		int FIVE_SECONDS = 5000;
		//	Create a timer.
        timer = new Timer(FIVE_SECONDS, new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
            	SwingWorker worker = 
                    new SwingWorker() {
            		
            		@Override
                    public Vector<String[]> construct() {
                    	Vector<String[]> tasks
                    		= Mainframe.this.getVisibleTaskList();
                      return tasks;
                    }
                    
                    @Override
                    public void finished() {
                    	try {
							Mainframe.this.updateTable(getVisibleTable(),
									(Vector<String[]>)get());
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (ExecutionException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
                    }
                 };
                 worker.start();
            }
        });
        
        updateControls();
        
        // Start the timer
        timer.start();
	}

	/**
	 * This method initializes jContentPane
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJContentPane() {
		if (jContentPane == null) {
			jContentPane = new JPanel();
			jContentPane.setLayout(new BorderLayout());
			jContentPane.add(getTopJPanel(), BorderLayout.PAGE_START);
			jContentPane.add(getJTabbedPane(), BorderLayout.CENTER);
			jContentPane.add(getBottomJPanel(), BorderLayout.PAGE_END);

			// set the ctrl-tab key to navigate through the tabs (issue=21)
			final int FORWARD = KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS;

			Action nextTabAction = new AbstractAction("nextTab") {
				public void actionPerformed(ActionEvent evt) {
					forwardTab();
				}
				private static final long serialVersionUID = -1124827786403794503L;
			};
			
			//remove the mapping of ctrl-tab to forward traversal
			Set<KeyStroke> tabSet = Collections.singleton(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
			jContentPane.setFocusTraversalKeys(FORWARD, tabSet);
			InputMap map = jContentPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);

			// add the mapping
			KeyStroke ctrlTab = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.CTRL_DOWN_MASK);
			map.put(ctrlTab, nextTabAction.getValue(Action.NAME));
			jContentPane.getActionMap().put(nextTabAction.getValue(Action.NAME), nextTabAction);
		}
		return jContentPane;
	}

	/**
	 * This method initializes jTabbedPane	
	 * 	
	 * @return javax.swing.JTabbedPane	
	 */
	private JTabbedPane getJTabbedPane() {
		if (jTabbedPane == null) {
			jTabbedPane = new JTabbedPane();
			jTabbedPane.setName("jTabbedPane");
			jTabbedPane.addTab("Active downloads", null, getJScrollPaneRunning(), null);
			jTabbedPane.addTab("Paused downloads", null, getJScrollPanePaused(), null);
			jTabbedPane.addTab("Finished downloads", null, getJScrollPaneFinished(), null);
			//jTabbedPane.addTab(null, null, getTopJPanel(), null);
			jTabbedPane.addMouseListener(new MouseAdapter() {
				public void mouseClicked(MouseEvent e) {
					updateControls();
					updateVisibleTable();
				}
			});
			
			updateVisibleTable();
		}
		return jTabbedPane;
	}
	
	public void forwardTab() {
		switchTab(true);
	}

	public void backwardTab() {
		switchTab(false);
	}

	private void switchTab(boolean forward) {
		int nextIndex = forward ? +1 : -1;
		JTabbedPane thePane = getJTabbedPane();
		int tabCount = thePane.getTabCount();
		thePane.setSelectedIndex(
				// Java's modulo implementation won't return a positive remainder, so the index is
				// being increased by the tab count
				(tabCount + thePane.getSelectedIndex()+nextIndex) % tabCount);		
	}

	private void updateControls() {
		int selectedTab = getJTabbedPane().getSelectedIndex();
		
		getShareTimeJMenuItem().setVisible(selectedTab == 0);
		getUpJButton().setVisible(selectedTab == 0);
		getDownJButton().setVisible(selectedTab == 0);
		getMoveToJButton().setVisible(selectedTab == 0);
		getPauseJButton().setVisible(selectedTab == 0);
		getResumeJButton().setVisible(selectedTab == 1);
	}
	
	private Vector<String[]> getVisibleTaskList() 
	{
		int index = getJTabbedPane().getSelectedIndex();
		
		Vector<String[]> tasks = null;
		switch (index)
		{ 
			case 0:
				tasks = this.controller.getList(TorrentDownloadState.Running);
				break;
			case 1:
				tasks = this.controller.getList(TorrentDownloadState.Paused);
				break;
			case 2:
				tasks = this.controller.getList(TorrentDownloadState.Finished);
				break;
		}
		
		return tasks;
	}
	
	private JTable getVisibleTable() 
	{
		int index = getJTabbedPane().getSelectedIndex();
		
		JTable table = null;
		switch (index)
		{ 
			case 0:
				table = getJTableRunning();
				break;
			case 1:
				table = getJTablePaused();
				break;
			case 2:
				table = getJTableFinished();
				break;
		}
		
		return table;
	}
	
	private void updateVisibleTable() 
	{
		updateTable(getVisibleTable(), getVisibleTaskList());
	}
	
	private void updateTable(JTable table, Vector<String[]> tasks) {
		int rowCount = tasks.size();
		
		DefaultTableModel model = (DefaultTableModel)table.getModel();
		
		while (model.getRowCount() > rowCount)
		{
			model.removeRow(tasks.size());
		}
		for	(int i = 0; i < rowCount; i++)
		{
			try
			{
				if (i < table.getRowCount())
				{
					model.setValueAt(i + 1, i, 0);
					if (i < tasks.size())
					{
						for	(int j = 0; j < tasks.elementAt(i).length && j < model.getColumnCount(); j++)
						{
							model.setValueAt(tasks.elementAt(i)[j], i, j + 1);
						}
					} else
					{
						for	(int j = 1; j < table.getColumnCount(); j++)
						{
							model.setValueAt("", i, j);
						}
					}
				} else
				{
					
					String[] tmp = (String[]) ArrayUtils.add(
							tasks.elementAt(i), 0, Integer.toString(i + 1)); 
					model.addRow(tmp);
				}
			} catch (Exception e)
			{}
		}
	}

	private void getRunningList() 
	{
		Vector<String[]> tasks
			= this.controller.getList(TorrentDownloadState.Running);
		updateTable(getJTableRunning(), tasks);
	}
	
	/**
	 * This method initializes jList1	
	 * 	
	 * @return javax.swing.JList	
	 */
	private JTable getJTableRunning() {
		if (jTableRunning == null) {
			String[] header = { "Pos", "Download task", "Status", "Size (Mb)", "Done (%)", "Download rate (kb/s)", "Seeders", "Peers", "ETA (hours)"};
			DefaultTableModel model = new DefaultTableModel(header, header.length)
			{
				private static final long serialVersionUID = 1L;

				public Class getColumnClass(int columnIndex)
			    {
					switch (columnIndex) {
						case 0:
						case 6:
						case 7:
							return Integer.class;
						case 3:
						case 4:
						case 5:
							return Integer.class;

					default:
						return super.getColumnClass(columnIndex);
					}
			     }
			};
			
			jTableRunning = new JTable(model);
			
			// Der TableRowSorter wird die Daten des Models sortieren 
	        TableRowSorter<DefaultTableModel> sorter
	        	= new TableRowSorter<DefaultTableModel>(model)
	        	{
		        	public Comparator<?> getComparator(int column)
		        	{
		        		if (column == 8)
		        		{
		        			return etaComparator;
		        		}
		        		return super.getComparator(column);
		        	}
	        	}; 
	        
			// Der TableRowSorter wird die Daten des Models sortieren 
//	        TableRowSorter<DefaultTableModel> sorter
//	        	= new TableRowSorter<DefaultTableModel>(model); 
	        // Der Sorter muss dem JTable bekannt sein 
	        jTableRunning.setRowSorter(sorter);
			
//			 Disable auto resizing to make the table horizontal scrollable
			jTableRunning.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
		}
		return jTableRunning;
	}
	
	private JTable getJTablePaused() {
		if (jTablePaused == null) {
			String[] header = { "Pos", "Download task", "Size (Mb)", "Done (%)", "Downloaded (Mb)", "Uploaded (Mb)", "Sharing duration", "Start time"};
			DefaultTableModel model = new DefaultTableModel(header, 20)
			{
				private static final long serialVersionUID = 1L;

				public Class getColumnClass(int columnIndex)
			    {
					switch (columnIndex) {
						case 0:
							return Integer.class;
						case 2:
						case 3:
						case 4:
						case 5:
							return Integer.class;

					default:
						return super.getColumnClass(columnIndex);
					}
			     }
			};
			jTablePaused = new JTable(model);
			
			// Der TableRowSorter wird die Daten des Models sortieren 
	        TableRowSorter<DefaultTableModel> sorter
	        	= new TableRowSorter<DefaultTableModel>(model)
	        	{
		        	public Comparator<?> getComparator(int column)
		        	{
		        		if (column == 7)
		        		{
		        			return dateComparator;
		        		}
		        		return super.getComparator(column);
		        	}
	        	}; 
	        // Der Sorter muss dem JTable bekannt sein 
	        jTablePaused.setRowSorter(sorter);
			
//			 Disable auto resizing to make the table horizontal scrollable
			jTablePaused.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
		}
		return jTablePaused;
	}
	
	private JTable getJTableFinished() {
		if (jTableFinished == null) {
			String[] header = { "Pos", "Download task", "Size (Mb)", "Time finished", "Downloaded (Mb)", "Uploaded (Mb)", "Sharing duration", "Start time"};
			DefaultTableModel model = new DefaultTableModel(header, 20)
			{
				private static final long serialVersionUID = 1L;

				public Class getColumnClass(int columnIndex)
			    {
					switch (columnIndex) {
						case 0:
							return Integer.class;
						case 2:
						case 4:
						case 5:
							return Integer.class;

					default:
						return super.getColumnClass(columnIndex);
					}
			     }
			};
			jTableFinished = new JTable(model);
			
			// Der TableRowSorter wird die Daten des Models sortieren 
			TableRowSorter<DefaultTableModel> sorter
        	= new TableRowSorter<DefaultTableModel>(model)
        	{
	        	public Comparator<?> getComparator(int column)
	        	{
	        		if (column == 3 || column == 7)
	        		{
	        			return dateComparator;
	        		}
	        		return super.getComparator(column);
	        	}
        	};
	        // Der Sorter muss dem JTable bekannt sein 
	        jTableFinished.setRowSorter(sorter);
			
//			 Disable auto resizing to make the table horizontal scrollable
			jTableFinished.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
		}
		return jTableFinished;
	}
	
	private JScrollPane getJScrollPanePaused() {
		
		if (jScrollPanePaused == null)
		{
			jScrollPanePaused = new JScrollPane(getJTablePaused());
		}
		
		return jScrollPanePaused;
	}
	
	private JScrollPane getJScrollPaneFinished() {
		
		if (jScrollPaneFinished == null)
		{
			jScrollPaneFinished = new JScrollPane(getJTableFinished());
		}
		
		return jScrollPaneFinished;
	}
	
	private JScrollPane getJScrollPaneRunning() {
		if (jScrollPaneRunning == null)
		{
			jScrollPaneRunning = new JScrollPane(getJTableRunning());
		}
		
		return jScrollPaneRunning;
	}
	
	class TorrentFileFilter extends FileFilter
	{
		@Override
		public boolean accept(File f) {
			if (f.isDirectory())
			{
				return true;
			}
			String ext = Utils.getExtension(f);
			return ext != null && ext.equals("torrent");
		}

		@Override
		public String getDescription() {
			return "Only torrents";
		}
	}
	
}  //  @jve:decl-index=0:visual-constraint="10,10"
